#![allow(non_snake_case)]

use std::collections::HashMap;

use crate::{
    components::{
        json_val_to_string, ButtonList, ButtonProp, CustomButton, DataEditInner, DataRange, EditColumn, EditRowState
    },
    shhr::SelectIds,
};

use icondata as i;
use leptos::{prelude::*, task::spawn_local};
use leptos_icons::*;
use serde_json::{Map, Value};
use shq_common::prelude::{
    AndOr, Column, ColumnCells, DataType, FliterItem, ListProps, Operator, WhereCause
};
use shq_web_controls::{
    use_message, ColumnShows, ConfirmButton, EditColValues, ListTable, MessageOptions, MessageVariant, OrderFilterModel, OrderFliterList, TableState, TreeItems
};
use thaw_utils::ArcOneCallback;
#[component]
pub fn ClientDataTable(
    #[prop(into)] menu_id: u32,
    #[prop(into)] conf_name: StoredValue<String>,
    #[prop(into)] title: Option<String>,
    #[prop(into)] list_props: StoredValue<ListProps>,
    #[prop(into)] field_props: StoredValue<Vec<Column>>,
    #[prop(into)] list_data: Vec<serde_json::Value>,
    #[prop(optional, into)] custom_buttons: StoredValue<ButtonList>,
    can_update: bool,
) -> impl IntoView {
    let inti_table_state = move || {
        field_props.with_value(|cols| TableState::from_columns(conf_name, cols, Some(list_data)))
    };
    let (table_state, _) = signal(inti_table_state());
    let list_data=table_state.with_untracked(|ts|ts.list_data());
    provide_context(table_state);
    let (edit_col_vals, _) = signal(
        field_props.with_value(|fp| EditColValues::from(&fp, list_data.with_untracked(|ld| ld.len()))),
    );
    provide_context(edit_col_vals);
    let tree_items = field_props.with_value(|cols| StoredValue::new(TreeItems::from(cols)));
    let display_sigal = table_state.with_untracked(|ts| ts.display());
    let header_cells = Memo::new(move |_| {
        display_sigal.with(|ds| {
            let display_cols: Vec<&str> = ds.iter().map(|s| s.as_str()).collect();
            field_props.with_value(|cols| ColumnCells::from_columns(cols, &display_cols))
        })
    });
    let select_ids = use_context::<WriteSignal<SelectIds>>();
    let sel_idxs = table_state.with_untracked(|ts| ts.selected_indexs());
    let pk_col = StoredValue::new(
        field_props
            .with_value(|cols| {
                cols.iter()
                    .find(|c| c.is_pk)
                    .map(|c| c.name.clone().unwrap_or_default())
            })
            .expect("没有找到主键列"),
    );
    Effect::new(move || {
        if let Some(select_ids) = select_ids {
            let pk_col = pk_col.get_value();
            let ids = sel_idxs.with(|idxs| {
                list_data.with_untracked(|ld| {
                    let ids: Vec<u64> = idxs
                        .iter()
                        .map(|i| ld[*i].get(&pk_col).and_then(Value::as_u64).unwrap_or(0))
                        .collect();
                    ids
                })
            });
            select_ids.set(SelectIds::from(ids));
        }
    });
    let multi_sel = list_props.with_value(|lp| lp.multi_select);
    let footer_show_props = list_props.with_value(|lp| lp.footer_show_props);
    view! {
        <Toolbar menu_id=menu_id
            conf_name=conf_name
            title=title
            columns=field_props
            custom_buttons/>
        <ListTable header_cells multi_sel cell_border=true footer_show_props can_update/>
        <OrderFilterModel header_cells opts_map=Memo::new(|_| HashMap::new())/>
        <ColumnShows tree_items/>
        <OrderFliterList tree_items/>
    }
}

#[component]
pub fn Toolbar(
    #[prop(into)] menu_id: u32,
    #[prop(into)] conf_name: StoredValue<String>,
    #[prop(into)] title: Option<String>,
    #[prop(into)] columns: StoredValue<Vec<Column>>,
    custom_buttons: StoredValue<ButtonList>,
) -> impl IntoView {
    let mess = use_message();
    let table_state =
        use_context::<ReadSignal<TableState>>().expect("ReadSignal<TableState>在上下文中不存在");
    let list_data=table_state.with_untracked(|ts|ts.list_data());
    let edit_col_vals = use_context::<ReadSignal<EditColValues>>();
    let title = StoredValue::new(title.unwrap_or_default());
    let edit_columns: Vec<EditColumn> = columns.with_value(|cols| {
        cols.iter()
            .filter(|p| {
                p.edit_props
                    .as_ref()
                    .map_or(false, |ep| ep.is_show_in_toolbar)
            })
            .map(|c| c.into())
            .collect::<Vec<_>>()
    });
    let pk_col = StoredValue::new(
        columns
            .with_value(|cols| {
                cols.iter()
                    .find(|c| c.is_pk)
                    .map(|c| c.name.clone().unwrap_or_default())
            })
            .expect("没有找到主键列"),
    );
    let field_props = StoredValue::new(edit_columns);
    let button_props = vec![ButtonProp::new(
        "查询".to_string(),
        i::AiSaveOutlined,
        ArcOneCallback::new(move |vals: Vec<(String, Value)>| {
            for (k, v) in vals.into_iter() {
                table_state.with_untracked(|ts| {
                    let operator = field_props.with_value(|fp| {
                        if let Some(col) = fp.iter().find(|c| c.name == k) {
                            if col.data_type == DataType::String {
                                Operator::Like
                            } else {
                                Operator::Eq
                            }
                        } else {
                            Operator::Eq
                        }
                    });
                    let sval = json_val_to_string(&v);
                    let fi = FliterItem {
                        operator,
                        value: Some(sval),
                    };
                    let wc = WhereCause::from(k.clone(), vec![fi], AndOr::And);
                    ts.set_filter(&k, Some(wc));
                })
            }
        }),
    )];
    let can_edit = edit_col_vals
        .map(|ecv| ecv.with_untracked(|ec| !ec.edit_cols.is_empty()))
        .unwrap_or_default();
    let (edit_row_data, _) =
        signal(field_props.with_value(|cols| EditRowState::from_columns(&cols,vec![])));
    let default_buttons = move || {
        if can_edit {
            let pk_col = pk_col.get_value();
            Some(view! {<button class="btn btn-primary" on:click=move|_|{
                let update_rows=list_data.with_untracked(|ld| {
                    edit_col_vals.unwrap().with_untracked(|ecv| {
                        ecv.get_update_rows(&pk_col, &ld)
                    })
                });
                if !update_rows.is_empty() {
                    spawn_local(async move {
                        match update_table_rows(menu_id, conf_name.get_value(), update_rows).await {
                            Ok(msgs) => {
                                table_state.with_untracked(|ts| {
                                    ts.set_curr_cell(None);
                                });
                                if !msgs.is_empty(){
                                    edit_col_vals.unwrap().with_untracked(|ecv| {
                                        msgs.into_iter().for_each(|(i, msg_map)| {
                                            for (k,v) in msg_map.into_iter() {
                                                ecv.set_error_msg(i, &k, Some(v));
                                            }
                                        });
                                    });
                                    mess.create(
                                        "保存失败，请查看表格中红色提示",
                                        MessageVariant::Success,
                                        MessageOptions::default(),
                                    )
                                }else{
                                    mess.create(
                                        "保存成功",
                                        MessageVariant::Success,
                                        MessageOptions::default(),
                                    )
                                };
                            }
                            Err(e) => {
                                mess.create(
                                    format!("更新失败: {}", e),
                                    MessageVariant::Success,
                                    MessageOptions::default(),
                                );
                            }
                        }
                    });
                }
            }>保存</button>})
        } else {
            None
        }
    };
     let custom_button_view = move |cb: CustomButton| {
        let extract_data = |d: &Value| {
            if let Some(mv) = d.as_object() {
                let mv = cb
                    .data_col_names
                    .iter()
                    .fold(Map::new(), |mut m, col_name| {
                        if let Some(v) = mv.get(*col_name) {
                            m.insert(col_name.to_string(), v.clone());
                        }
                        m
                    });
                Value::Object(mv)
            } else {
                Value::Null
            }
        };
        let ret_vals = {
            let ret_vals: Vec<Value> = match cb.data_range {
                DataRange::All => table_state.with_untracked(|ts| {
                    ts.list_data()
                        .with(|ld| ld.iter().map(|d| extract_data(d)).collect())
                }),
                DataRange::Selected => table_state.with_untracked(|ts| {
                    ts.selected_indexs().with(|idxs| {
                        idxs.iter()
                            .filter_map(|idx| {
                                ts.list_data()
                                    .with(|ld| ld.get(*idx).map(|d| extract_data(d)))
                            })
                            .collect()
                    })
                }),
                DataRange::None => Vec::new(),
            };
            StoredValue::new(ret_vals)
        };
        match cb.confirm_text {
            Some(ct) => {
                view! {
                    <ConfirmButton text=cb.text class="btn btn-primary" message=ct
                        on_click=move|| {
                            if (cb.data_range == DataRange::Selected) && ret_vals.with_value(|tv| tv.is_empty()) {
                                mess.create("请先选择要操作数据行", MessageVariant::Error, MessageOptions::default());
                                return false;
                            }
                            true
                        }
                        on_ok=move|_|{
                            (cb.on_click)(ret_vals.get_value());
                        }/>
                }.into_any()
            },
            None=>{
                view! {
                    <button class="btn btn-primary" on:click=move |_| {
                        if (cb.data_range == DataRange::Selected) && ret_vals.with_value(|tv| tv.is_empty()) {
                            mess.create("请先选择要操作数据行", MessageVariant::Error, MessageOptions::default());
                            return;
                        }
                        (cb.on_click)(ret_vals.get_value());
                    }>
                        {if let Some(icon)=cb.icon {
                            Some(view! {<Icon icon=icon width="20" height="20"/>})
                        } else {
                            None
                        }}
                        {cb.text}
                    </button>
                }.into_any()
            }
        }
    };
    let custom_buttons_view = move || {
        custom_buttons.with_value(|cbs| {
            cbs.get_custom_buttons().map(move |b| {
                custom_button_view(b.clone())
            }).collect_view()
        })
    };
    let field_props=Signal::derive(move||field_props.get_value());
    view! {
        <div class="flex-none navbar bg-base-100 rounded-t-lg">
            <div class="navbar-start">
                <a class="btn btn-ghost normal-case text-base">{title.get_value()}</a>
            </div>
            <div class="navbar-center hidden lg:flex">
                <DataEditInner field_props edit_row_data button_props/>
            </div>
            <div class="navbar-end flex gap-4">
                <Show when=move||custom_buttons.with_value(|butts|!butts.is_empty()) fallback=move|| {
                    {default_buttons}
                }>
                    {custom_buttons_view}
                </Show>
            </div>
        </div>
    }
}

#[server(UpdateTableRows, "/api/protect")]
async fn update_table_rows(
    menu_id: u32,
    conf_name: String,
    rows: Vec<Map<String, Value>>,
) -> Result<Vec<(usize, HashMap<String, String>)>, ServerFnError> {
    if rows.is_empty() {
        return Ok(vec![]);
    }
    use crate::auth::get_user_context_with_auth;
    let user = get_user_context_with_auth(menu_id, shq_common::prelude::AuthType::Select).await?;
    let ps = shq_pas_backend::PageService::new(&conf_name, user.user_id, menu_id);
    let mut msgs: Vec<(usize, HashMap<String, String>)> = Vec::new();
    for (i, row_data) in rows.iter().enumerate() {
        let msg = ps
            .validate_regex_rule(row_data)
            .map_err(|err| ServerFnError::new(err.to_string()))?;
        if !msg.is_empty() {
            msgs.push((i, msg));
        }
    }
    if !msgs.is_empty() {
        return Ok(msgs);
    }
    let fname = rows[0].iter().next().unwrap().0.clone();
    let res = ps.update_table_rows(rows).await;
    match res {
        Ok(msgs) => {
            let msgs = msgs
                .into_iter()
                .map(|(i, msg)| {
                    let mut error_map = HashMap::new();
                    error_map.insert(fname.clone(), msg);
                    (i, error_map)
                })
                .collect();
            return Ok(msgs);
        }
        Err(e) => {
            return Err(ServerFnError::ServerError(e.to_string()));
        }
    }
}
